#include "SaveList.h"

using namespace std;
using std::string;

class VariableListItem{
public: 
 Matrix* Data;
 string Name;
 VariableListItem* pNextVariableListItem; 
 
 ~VariableListItem()
 { Name.clear(); }
 
 VariableListItem()
 {
  Data = NULL;
  Name = " ";
  pNextVariableListItem = NULL;
 }
};

VariableListItem* pHeadVariableListItem = NULL;

bool ClearGlobalVariableList( void )
{
 VariableListItem* pCurrent = pHeadVariableListItem; 
 while( pCurrent->pNextVariableListItem )
 {
  pHeadVariableListItem = pCurrent->pNextVariableListItem;
  delete pCurrent;
  pCurrent = pHeadVariableListItem; 
 }
 return true;
}

bool AddToGlobalVariableList( Matrix* DataSource, string DataName )
{
 VariableListItem* pNew;
 pNew = new VariableListItem; 
 pNew->Data = DataSource;
 pNew->Name = DataName; 
 
 if( pHeadVariableListItem )
 { 
  VariableListItem* pCurrent = pHeadVariableListItem; 
  while( pCurrent->pNextVariableListItem )
  {
   pCurrent = pCurrent->pNextVariableListItem;
  }
  pCurrent->pNextVariableListItem = pNew; 
 }
 else
 { pHeadVariableListItem = pNew; }
 return true;
}

Matrix* FindGlobalVariableAddress( string DataName )
{
 Matrix* output = NULL;
 VariableListItem* pCurrent = pHeadVariableListItem;
 bool Done = false;
 while( pCurrent && !Done )
 {
  if( strcmp( (pCurrent->Name).c_str() , DataName.c_str() ) == 0 )
  { output = pCurrent->Data; Done = true; }
  pCurrent = pCurrent->pNextVariableListItem;  
 }
 return output;
}

bool BuildGlobalVariableList( void )
{
 extern Matrix X;
 extern Matrix Y;
 extern Matrix Phi;
 extern Matrix PhiNecrotic;
 extern Matrix PhiQuiescent;

 extern Matrix Oxygen;
 extern Matrix Pressure;
 extern Matrix MDE;
 extern Matrix TAF;
 
 extern Matrix MDEdiffusivity;
 extern Matrix ECM;
 extern Matrix Mobility;
 extern Matrix Structure;

 extern Matrix OxygenSource; 

 extern Matrix BloodVesselIndicatorX;
 extern Matrix BloodVesselIndicatorY;
 extern Matrix BloodVesselIndicator;
 extern Matrix BloodVesselRadius;
 extern Matrix Flow;
 extern Matrix HaematocritLevel;
 extern Matrix SproutTips;
 extern Matrix VesselCells;
 extern Matrix MapIndicator;
 extern Matrix MapVesselCells;
 extern Matrix MapSproutTips;
 extern Matrix MapFlow;
 extern Matrix MapPressure;
 extern Matrix MapRadius;
 extern Matrix VEGFR;
 extern Matrix Notch;
 extern Matrix Delta4;
 extern Matrix BloodVesselRadiusXdir;
 extern Matrix BloodVesselRadiusYdir;
 extern Matrix BloodVesselPressureX;
 extern Matrix BloodVesselPressureY;
 extern Matrix InterPressure;
 //extern Matrix OsmoticDif;
 extern Matrix CellColor;
 extern Matrix LymIndicator;
 extern Matrix Permeability;
 extern Matrix WallGradient;
 extern Matrix Conduct;
 extern Matrix SproutAge;
 extern Matrix VesselAge;
 extern Matrix OxygenDiffusivity;
 extern Matrix BloodVesselDensity;
 extern Matrix VisX, VisY, Vis; //viscosity
 extern Matrix BloodNodePressure;
 extern Matrix Speed;
 extern Matrix NecroticSpeed;
 extern Matrix PressureGradientX;
 extern Matrix PressureGradientY;
 extern Matrix NormalX;
 extern Matrix NormalY;
 extern Matrix FlowX, FlowY;
 extern Matrix WssX, WssY, Wss;
 extern Matrix Stability;
 extern Matrix PreExist;
 extern Matrix TracerX, TracerY, Tracer, TracerS, TracerT, TracerD, Debuger, ShearRate, SrtX, SrtY, Macrof, MacrofD, Leak, HomPressure;
 extern Matrix Sp,Sm,Ss,Swss, Svar;
 extern Matrix SpX,SmX,SsX,SwssX, SvarX;
 extern Matrix SpY,SmY,SsY,SwssY, SvarY;
 extern Matrix ConductX, ConductY; //conductivity
 extern Matrix OutPressure;
 extern Matrix TotalPressure;
 extern Matrix Drainage;
 
 extern Matrix ECMtaxisCoefficient;
 extern Matrix ECMgradientX;
 extern Matrix ECMgradientY;
 
 extern Matrix Original_ECM;
 
 // Clear out the list, if it already exists. 
 
 if( pHeadVariableListItem )
 { ClearGlobalVariableList(); }
 
 AddToGlobalVariableList( &X, "X" );
 AddToGlobalVariableList( &Y, "Y" );
 AddToGlobalVariableList( &Phi, "Phi" );
 AddToGlobalVariableList( &PhiQuiescent, "PhiQuiescent" );
 AddToGlobalVariableList( &PhiNecrotic , "PhiNecrotic" );
 
 AddToGlobalVariableList( &Oxygen, "Oxygen" );
 AddToGlobalVariableList( &Pressure, "Pressure" );
 AddToGlobalVariableList( &MDE, "MDE" );
 AddToGlobalVariableList( &TAF, "TAF" );
 
 AddToGlobalVariableList( &MDEdiffusivity, "MDEdiffusivity" );
 AddToGlobalVariableList( &ECM, "ECM" );
 AddToGlobalVariableList( &Original_ECM, "Original_ECM" );
 AddToGlobalVariableList( &Mobility, "Mobility" );
 AddToGlobalVariableList( &Structure, "Structure" );
 
 AddToGlobalVariableList( &ECMtaxisCoefficient, "ECMtaxisCoefficient" );
 AddToGlobalVariableList( &ECMgradientX, "ECMgradientX" );
 AddToGlobalVariableList( &ECMgradientY, "ECMgradientY" );
 
 AddToGlobalVariableList( &OxygenDiffusivity, "OxygenDiffusivity" );
 AddToGlobalVariableList( &BloodVesselIndicatorX, "BloodVesselIndicatorX");
 AddToGlobalVariableList( &BloodVesselIndicatorY, "BloodVesselIndicatorY");
 AddToGlobalVariableList( &BloodVesselIndicator, "BloodVesselIndicator");
 AddToGlobalVariableList( &BloodVesselRadius, "BloodVesselRadius");
 AddToGlobalVariableList( &Flow, "Flow");
  AddToGlobalVariableList( &VesselCells, "VesselCells");
  AddToGlobalVariableList( &MapIndicator, "MapIndicator");
  AddToGlobalVariableList( &MapVesselCells, "MapVesselCells");
  AddToGlobalVariableList( &MapSproutTips, "MapSproutTips");
   AddToGlobalVariableList( &MapFlow, "MapFlow");
   AddToGlobalVariableList( &MapRadius, "MapRadius");
   AddToGlobalVariableList( &MapPressure, "MapPressure");
  AddToGlobalVariableList( &VEGFR, "VEGFR");
  AddToGlobalVariableList( &Notch, "Notch");
  AddToGlobalVariableList( &Delta4, "Delta4");
 AddToGlobalVariableList( &HaematocritLevel, "HaematocritLevel");
 AddToGlobalVariableList( &SproutTips, "SproutTips");
 AddToGlobalVariableList( &SproutAge, "SproutAge");
 AddToGlobalVariableList( &VesselAge, "VesselAge");
 AddToGlobalVariableList( &OxygenDiffusivity, "OxygenDiffusivity");
 AddToGlobalVariableList( &BloodVesselDensity, "BloodVesselDensity");

 AddToGlobalVariableList( &Speed, "Speed");
 AddToGlobalVariableList( &NecroticSpeed, "NecroticSpeed");
 AddToGlobalVariableList( &PressureGradientX, "PressureGradientX");
 AddToGlobalVariableList( &PressureGradientY, "PressureGradientY");
 AddToGlobalVariableList( &NormalX, "NormalX");
 AddToGlobalVariableList( &NormalY, "NormalY");

 AddToGlobalVariableList( &BloodVesselRadiusXdir, "BloodVesselRadiusXdir");
 AddToGlobalVariableList( &BloodVesselRadiusYdir, "BloodVesselRadiusYdir");
 AddToGlobalVariableList( &VisX, "VisX");
 AddToGlobalVariableList( &VisY, "VisY");
 AddToGlobalVariableList( &Vis, "Vis");
 AddToGlobalVariableList( &VisX, "VisX");
 AddToGlobalVariableList( &VisY, "VisY");
 AddToGlobalVariableList( &FlowX, "FlowX");
 AddToGlobalVariableList( &FlowY, "FlowY");
 AddToGlobalVariableList( &WssX, "WssX");
 AddToGlobalVariableList( &WssY, "WssY");
  AddToGlobalVariableList( &Wss, "Wss");
    AddToGlobalVariableList( &Stability, "Stability");
	AddToGlobalVariableList( &PreExist, "PreExist");
  AddToGlobalVariableList( &Tracer, "Tracer");
  AddToGlobalVariableList( &TracerS, "TracerS");
  AddToGlobalVariableList( &TracerT, "TracerT");
   AddToGlobalVariableList( &Macrof, "Macrof");
      AddToGlobalVariableList( &MacrofD, "MacrofD");
  AddToGlobalVariableList( &TracerD, "TracerD");
	 AddToGlobalVariableList( &Debuger, "Debuger");
    AddToGlobalVariableList( &ShearRate, "ShearRate");
	 AddToGlobalVariableList( &Svar, "Svar");
	 AddToGlobalVariableList( &Swss, "Swss");
	  AddToGlobalVariableList( &Sp, "Sp");
	   AddToGlobalVariableList( &Sm, "Sm");
	    AddToGlobalVariableList( &Ss, "Ss");
	AddToGlobalVariableList( &SvarX, "SvarX");
	AddToGlobalVariableList( &SwssX, "SwssX");
	AddToGlobalVariableList( &SpX, "SpX");
	AddToGlobalVariableList( &SmX, "SmX");
	AddToGlobalVariableList( &SsX, "SsX");
	AddToGlobalVariableList( &SvarY, "SvarY");
	AddToGlobalVariableList( &SwssY, "SwssY");
	AddToGlobalVariableList( &SpY, "SpY");
	AddToGlobalVariableList( &SmY, "SmY");
	AddToGlobalVariableList( &SsY, "SsY");
	 AddToGlobalVariableList( &SrtX, "SrtX");
	  AddToGlobalVariableList( &SrtY, "SrtY");
   AddToGlobalVariableList( &TracerS, "TracerS");
    AddToGlobalVariableList( &Tracer, "Tracer");
	  AddToGlobalVariableList( &TracerX, "TracerX");
    AddToGlobalVariableList( &TracerY, "TracerY");
		 AddToGlobalVariableList( &Leak, "Leak");
		  AddToGlobalVariableList( &HomPressure, "HomPressure");
 AddToGlobalVariableList( &ConductX, "ConductX");
 AddToGlobalVariableList( &ConductY, "ConductY");
 AddToGlobalVariableList( &BloodNodePressure, "BloodNodePressure");
 AddToGlobalVariableList( &BloodVesselPressureX, "BloodVesselPressureX");
 AddToGlobalVariableList( &BloodVesselPressureY, "BloodVesselPressureY");
 AddToGlobalVariableList( &InterPressure, "InterPressure");
  AddToGlobalVariableList( &CellColor, "CellColor");
 AddToGlobalVariableList( &LymIndicator, "LymIndicator");
 AddToGlobalVariableList( &Permeability, "Permeability");
 AddToGlobalVariableList( &WallGradient, "WallGradient");
 AddToGlobalVariableList( &Conduct, "Conduct");
	AddToGlobalVariableList( &OutPressure, "OutPressure" );
	AddToGlobalVariableList( &Drainage, "Drainage" );
	AddToGlobalVariableList( &TotalPressure, "TotalPressure" );

 
 return true;
}

void DisplayGlobalVariableList( void )
{
 cout << "The following global variables are available to the advanced save routines:" << endl;
 VariableListItem* pCurrent = pHeadVariableListItem;
 while( pCurrent )
 {
  cout << (pCurrent->Name).c_str() << " at memory address " << pCurrent->Data << endl;
  pCurrent = pCurrent->pNextVariableListItem;
 }
 cout << endl;
 return;
}


class SaveListItem{
public:
 string VariableName;
 string OutputName;
 Matrix* DataSource;
 SaveListItem* pNextSaveListItem;
 
 ~SaveListItem()
 {
  VariableName.clear(); 
  OutputName.clear(); 
  DataSource = NULL; 
  pNextSaveListItem = NULL; 
 }
 SaveListItem()
 {
  VariableName = " "; 
  OutputName = " ";
  DataSource = NULL;  
  pNextSaveListItem = NULL; 
 }
 
 void Display( void )
 {
  cout << "source:      " << VariableName.c_str() << endl
       << "output name: " << OutputName.c_str() << endl << endl;
  return;
 }
};

SaveListItem* pHeadSaveListItem = NULL;
char SaveMode = 'B';

bool ClearSaveList( void )
{
 SaveListItem* pCurrent = pHeadSaveListItem;
 while( pCurrent )
 {
  pHeadSaveListItem = pCurrent->pNextSaveListItem;
  delete pCurrent;
  pCurrent = pHeadSaveListItem; 
 }

 return true;
}

bool AddToSaveList( const char* SourceName, const char* OutputName )
{ 
 SaveListItem* pNew;
 pNew = new SaveListItem;
 
 pNew->VariableName = SourceName;
 pNew->OutputName = OutputName;
 pNew->DataSource = FindGlobalVariableAddress( pNew->VariableName );
 
 SaveListItem* pCurrent = pHeadSaveListItem;
 if( !pCurrent )
 { pHeadSaveListItem = pNew; }
 else
 {
  while( pCurrent->pNextSaveListItem )
  { pCurrent = pCurrent->pNextSaveListItem; }
  pCurrent->pNextSaveListItem = pNew; 
 }
 return true;
}

bool InitializeSaveListFromXML( const char* XML_file_name )
{
 // load the XML document 
 
 TiXmlDocument doc( XML_file_name );
 if( !doc.LoadFile() )
 {
  cout << "Error: XML save list file " << XML_file_name << " not found, or failed to load!" << endl;
  return false; 
 }

 TiXmlHandle hDoc(&doc);
 TiXmlElement* pElem;
 TiXmlHandle hRoot(0);

 pElem=hDoc.FirstChildElement().Element();
 // should always have a valid root but handle gracefully if it does
 if( !pElem )
 { 
  cout << "Error: XML save list file " << XML_file_name << " is messed up!" << endl;
  return false;
 }

 TiXmlElement* pCurrentElement = NULL;
 
 // Look for all defined tissues and add them to the linked list structure 

 pCurrentElement = FindChildElement( pElem , "variable" );
 
 SaveListItem* pCurrent = pHeadSaveListItem;
  
 while( pCurrentElement )
 {
  SaveListItem* new_item;
  new_item = new SaveListItem;
  new_item->VariableName = GetStringChildElement( pCurrentElement, "source");
  new_item->OutputName   = GetStringChildElement( pCurrentElement, "output_name");
  if( (new_item->OutputName).length() == 0 )
  {
   new_item->OutputName = new_item->VariableName;
   cout << "Warning: Output name not specified for variable " << (new_item->VariableName).c_str() << endl
        << "         in the XML file " << XML_file_name << "." << endl
		<< "         Using a reasonable guess of " << (new_item->OutputName).c_str() << " ... " << endl << endl;
  }
  
  new_item->DataSource = FindGlobalVariableAddress( new_item->VariableName );  
 
  if( !pCurrent )
  { pHeadSaveListItem = new_item; }
  else
  { pCurrent->pNextSaveListItem = new_item; }
  pCurrent = new_item;
  
  pCurrentElement = FindNextChildElement( pElem , pCurrentElement, "variable" );
 }

 return false; 
}

void DisplaySaveList( void )
{
 SaveListItem* pCurrent = pHeadSaveListItem;
 while( pCurrent )
 {
  pCurrent->Display();
  pCurrent = pCurrent->pNextSaveListItem;
 }

 return;
}

bool SaveVariables( const char* path, double time )
{
 SaveListItem* pCurrent = pHeadSaveListItem;
 while( pCurrent )
 {
  string output_name = path;
  output_name += "/";
  output_name += pCurrent->OutputName;
  
  char* temp_char;
  temp_char = new char [1025];
  sprintf( temp_char , "_%3.8f.dat" , time );
  
  string end_of_output_name = temp_char;
  int i = end_of_output_name.find('.',0);
  end_of_output_name[i] = '_';
  
  output_name += end_of_output_name;
  const char* output_name_char_array = output_name.c_str();
  
  cout << "Saving variable " << (pCurrent->VariableName).c_str() << " to file " 
       << output_name.c_str() << " ... " << endl;
	   //SaveMode = 'T';//add here
  if( SaveMode == 'B' )
  { 
   if( pCurrent->DataSource )
   { (pCurrent->DataSource)->WriteToFile( output_name_char_array ); }
   else
   { cout << "\tError: Could not find data source!" << endl; }
  }
  else
  { 
   if( pCurrent->DataSource )
   { (pCurrent->DataSource)->WriteToTextFile( output_name_char_array ); }
   else
   { cout << "\tError: Could not find data source!" << endl; }
  }
 
  pCurrent = pCurrent->pNextSaveListItem; 
 }

 return true;
}

bool SaveVariables( const char* path )
{
 SaveListItem* pCurrent = pHeadSaveListItem;
 while( pCurrent )
 {
  string output_name = path;
  output_name += "/";
  output_name += pCurrent->OutputName;
  output_name += "_resume.dat";
  
  const char* output_name_char_array = output_name.c_str();
  
  cout << "Saving variable " << (pCurrent->VariableName).c_str() 
       << " to file " << output_name.c_str() << " ... " << endl;
   SaveMode = 'T';//add here
  if( SaveMode == 'B' )
  { 
   if( pCurrent->DataSource )
   { (pCurrent->DataSource)->WriteToFile( output_name_char_array ); }
   else
   { cout << "\tError: Could not find data source!" << endl; }
  }
  else
  { 
   if( pCurrent->DataSource )
   { (pCurrent->DataSource)->WriteToTextFile( output_name_char_array ); }
   else
   { cout << "\tError: Could not find data source!" << endl; }
  }
 
  pCurrent = pCurrent->pNextSaveListItem; 
 }

 return true;
}


bool SetSaveMode( char SaveType )
{
 char temp = toupper( SaveType );
 if( temp  != 'B' && temp  != 'T' )
 { 
  cout << "Error: unrecognized format. Choosing safe 'T' (text) format ... " << endl; 
  //SaveMode = 'T';
  return false;
 }
 SaveMode = temp;
 return true;
}

char GetSaveMode( void )
{ return SaveMode; }

